import copy
import math
import random
import sys
import time

import matplotlib.pyplot as plt
import numpy as np
from NSGA2 import *
from crossover import *
from encode import *
from init import read
from mpl_toolkits.mplot3d import Axes3D
from mutation import *
from selection import *

# fileName = f"data/n01_2.mm"
fileName = f"data/example"
pop_size = 30  # 种群规模
pop = {}
chosen3d = False  # 3d散点图
resourceNum = 0  # 资源种类数
modeNum = 0  # 模式种类数
pc = 0.9  # 交叉概率
pm = 0.1  # 变异概率
maxGen = 50  # 最大进化代数
maxResources = []  # 最大资源限量
step = []  # 资源增加步长
upBoardStep = 3
increased = []  # 资源增加量
stepCost = []  # 单位时间增加单位资源的成本
result = set()  # 结果集
cost = []
increasedCost = []


def init():
    """
    算法主函数
    :return:
    """
    """读取算例"""
    global result
    actNum, actList, resourceNum, resources, modeNum = read(fileName)
    initResources = resources.copy()  # 资源备份
    """一部分论文和算例中未出现的变量，随机代替,包括资源增加步长与单位时间增加单位资源的成本"""
    """计算上界"""
    maxResources = CPMBasedHeuristic(actList, resources, modeNum)
    # print(maxResources)
    """计算步长与循环终止条件"""
    np.random.seed(12)
    for i in range(0, resourceNum):
        # print(maxResources[i])
        # print(initResources[i])
        tmp_step = np.ceil((maxResources[i] - initResources[i]) / upBoardStep)
        step.append(tmp_step)
        # print("step:", step)
        stepCost.append(8)
        # stepCost.append(np.random.randint(1, 50))
    """柔性资源可用量"""
    for i in range(0, upBoardStep):
        increased.clear()
        increasedCost.clear()
        for k in range(0, resourceNum):
            increased.append(step[k] * i)
            increasedCost.append(increased[k] * stepCost[k])  # 未乘项目工期
        for j in range(0, resourceNum):
            resources[j] = increased[j] + initResources[j]
        """创建画布"""
        # plt.figure(i, dpi=150, figsize=(10, 16))
        plt.figure(i)
        plt.title("resources{}".format(resources))
        if chosen3d:
            ax = plt.subplot(projection="3d")
        """当前资源增量下开始"""
        # print(
        #     "====================================当前增量为{}=====================================================".format(
        #         increased))
        """生成初始种群"""
        pop = encode(modeNum, pop_size, actNum, actList, resourceNum, resources, increasedCost)
        """进入遗传算法循环"""
        gen = 0
        tmp_res = set()
        while gen < maxGen:
            """当前代次"""
            gen = gen + 1
            """保留亲代种群"""
            ancestor = pop.copy()
            for indiv in ancestor:
                indiv.fit(actList, resources, increasedCost)
            """交叉"""
            pop = crossover(pop, pc, actList, resources)
            """变异"""
            pop = mutation(pop, pm, actList, resources)
            """计算子代目标函数"""
            for indiv in pop:
                # print(indiv)
                indiv.fit(actList, resources, increasedCost)
            """合并亲子两代并就行非支配排序"""
            tmp = pop + ancestor
            # print(len(pop))
            """快速非支配排序"""
            tmp, paretoRate, paretoNum = NSGA2(tmp)
            """选择"""
            pop = binary_championship_selection(tmp)
            """当前代的结果绘图"""
            x = [gen] * len(pop)
            y1 = [a.duration for a in pop]
            y2 = [a.cost for a in pop]
            if chosen3d:
                # 三维图形
                ax.scatter(x, y1, y2)
                ax.set_xlabel("gen")
                ax.set_ylabel("duration")
                ax.set_zlabel("cost")
            else:
                # 工期散点
                plt.subplot(3, 1, 1)
                plt.xlabel("gen")
                plt.ylabel("duration")
                plt.scatter([gen] * len(y1), y1)
                # 成本散点
                plt.subplot(3, 1, 2)
                plt.xlabel("gen")
                plt.ylabel("cost")
                plt.scatter([gen] * len(y2), y2)
                # 亲子合并中间种群帕累托非支配比例
                plt.subplot(3, 1, 3)
                plt.xlabel("gen")
                plt.ylabel("paretoRate")
                plt.scatter(gen, paretoRate, marker='*')
                plt.grid(True, alpha=0.5, axis='both', linestyle=':')  # 添加网格线
            # print("generation： ", gen)
            # print("pop_size: ", len(pop))
            # print(pop)
            # for indiv in set(pop):
            #     print(indiv)
            # """当种群中解个数唯一时，退出遗传"""
            # if len(set(pop)) == 1:
            #     break
            # """当帕累托最优充满种群时，停止遗传迭代"""
            # if paretoRate == 1:
            #     break
        """保存散点图"""
        print(resources)
        plt.savefig("analysis/{}resources{}.png".format("3d-" if chosen3d else "2d-", resources))
        """将当前资源可用量下的结果去重后存入解集"""
        tmp_res = set(pop)  # 按属性去重，实现ep和hash方法
        print(
            "====================================当前增量为{}时的解集==============================================".format(
                increased))
        for indiv in tmp_res:
            print(indiv)
        result = result | tmp_res

    print("***********全部柔性资源的最终解集***************")
    print("***********全部柔性资源的最终解集***************")
    print("***********全部柔性资源的最终解集***************")
    print("***********全部柔性资源的最终解集***************")
    print("***********全部柔性资源的最终解集***************")

    """对最终解集进行排序"""
    result = list(result)
    result, res_paretoRate, paretoNum = NSGA2(result)
    # result.sort(key=lambda x: x.duration)
    # print(result)
    result = costImprove(result, paretoRate)
    for indiv in result:
        print(indiv)
    plt.figure()
    x = [a.duration for a in result]
    y = [a.cost for a in result]
    plt.title("result")
    plt.xlabel("duration")
    plt.ylabel("cost")
    # plt.annotate(txt, (a.resources[0] for a in result))
    plt.scatter(x, y)
    plt.savefig("analysis/result")


if __name__ == '__main__':
    """控制台内容输出到文件"""
    filename = "analysis/log.txt"
    standard_output = sys.stdout
    sys.stdout = open(filename, "w+")
    """计算程序运行时间"""
    start = time.time()
    init()
    end = time.time()
    print("程序运行时间：", end - start)
    """输出控制台日志"""
    sys.stdout = standard_output
    print(open(filename, "r+").read())
    """显示绘图"""
    plt.show()
